package org.catspaw.cherubim.text.stroke;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

import bak.pcj.map.CharKeyIntMap;
import bak.pcj.map.CharKeyIntOpenHashMap;
import bak.pcj.map.IntKeyIntMap;
import bak.pcj.map.IntKeyIntOpenHashMap;

public abstract class TextBasedStrokeDictionary implements StrokeDictionary {

	private IntKeyIntMap				codePointDict;
	private CharKeyIntMap				charDict;
	private Set<StrokeDictionary.Entry>	entrySet;
	private int							minStroke	= -1;
	private int							maxStroke	= -1;

	public TextBasedStrokeDictionary(File file) {
		Scanner scanner = null;
		try {
			scanner = new Scanner(file);
			init(scanner);
		} catch (FileNotFoundException e) {
			throw new IllegalArgumentException(e);
		} finally {
			if (scanner != null) {
				scanner.close();
			}
		}
	}

	public TextBasedStrokeDictionary(URL url) {
		init(url);
	}

	public TextBasedStrokeDictionary(String fileName) {
		URL url = TextBasedStrokeDictionary.class.getResource(fileName);
		init(url);
	}

	private void init(URL url) {
		InputStream is = null;
		try {
			is = url.openStream();
		} catch (IOException e) {
			throw new IllegalArgumentException(e);
		}
		if (is == null) {
			throw new IllegalArgumentException("no file for url: "
					+ url.getPath());
		}
		Scanner scanner = null;
		try {
			scanner = new Scanner(is);
			init(scanner);
		} finally {
			if (scanner != null) {
				scanner.close();
			}
		}
	}

	private void init(Scanner scanner) {
		codePointDict = new IntKeyIntOpenHashMap();
		charDict = new CharKeyIntOpenHashMap();
		entrySet = new HashSet<Entry>();
		while (scanner.hasNextLine()) {
			String line = scanner.nextLine();
			StrokeDictionary.Entry entry = parseLine(line);
			int stroke = entry.getStroke();
			codePointDict.put(entry.getCodePoint(), stroke);
			charDict.put(entry.getChar(), stroke);
			entrySet.add(entry);
			if (stroke > maxStroke || maxStroke < 0) {
				maxStroke = stroke;
			}
			if (stroke < minStroke || minStroke < 0) {
				minStroke = stroke;
			}
		}
	}

	protected abstract StrokeDictionary.Entry parseLine(String line);

	public int lookupStroke(int codePoint) {
		return codePointDict.get(codePoint);
	}

	public int lookupStroke(char c) {
		return charDict.get(c);
	}

	public int maxStroke() {
		return maxStroke;
	}

	public int minStroke() {
		return minStroke;
	}

	public int size() {
		return codePointDict.size();
	}

	public char[] chars() {
		return charDict.keySet().toArray();
	}

	public int[] codePoints() {
		return codePointDict.keySet().toArray();
	}

	public Set<Entry> entrySet() {
		return entrySet;
	}
}
